#include <windows.h>
#include <iostream>
#include <sstream>
#include <io.h>
#include <cstdlib>
using namespace std;

void GotoXY(int x,int y);
__int64 GetAllSize(LPCSTR lpszFileBaseName,int *nFileNum){
	__int64 qwSize = 0;
	ostringstream ostr;
	ostr<<lpszFileBaseName<<".split.part"<<1;
	for(int nCount = 2;!_access(ostr.str().c_str(),0);++nCount){
		HANDLE hFile = CreateFile(ostr.str().c_str(),GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,NULL);
		qwSize += GetFileSize(hFile,NULL);
		CloseHandle(hFile);
	 	ostr.str(""); 
		ostr<<lpszFileBaseName<<".split.part"<<nCount;
		++*nFileNum;
	}
	return qwSize;
}
void SetConsoleLoading(int nPos,int nMetaSplit){
	CONSOLE_SCREEN_BUFFER_INFO bInfo;
	HANDLE hSTD = GetStdHandle(STD_OUTPUT_HANDLE);
	GetConsoleScreenBufferInfo(hSTD,&bInfo);
	bInfo.dwCursorPosition.X = 0;
	GotoXY(bInfo.dwCursorPosition.X,bInfo.dwCursorPosition.Y);
	cout<<"                                      ";
	SetConsoleCursorPosition(hSTD,bInfo.dwCursorPosition);
	cout<<"Meta starts... [";
	for(int nCount = 0;nCount < nPos/(nMetaSplit/10);++nCount)
		cout<<"=";
	for(int nCount = 0;nCount < 10 - nPos/(nMetaSplit/10);++nCount)
		cout<<" ";
	cout<<"]("<<((nPos == nMetaSplit)?"":" ")<<nPos<<"/"<<nMetaSplit<<")";
}
void GotoXY(int x,int y){
	HANDLE hSTD = GetStdHandle(STD_OUTPUT_HANDLE);
	COORD xy;
	xy.X = x;
	xy.Y = y;
	SetConsoleCursorPosition(hSTD,xy);
}
void FlushConsole(int x,int y){
	GotoXY(x,y);
	cout<<"                                                                            "<<endl
		<<"                                                                            "<<endl
		<<"                                                                            "<<endl
		<<"                                                                            "<<endl
		<<"                                                                            "<<endl;
	GotoXY(x,y);
}

int main(int argc,char *argv[])
{
	int nSplitNum = 0,nMetaSplit = 100;
	system("cls");
	if(argc < 3){
		cout<<"The Syntax of command is error."<<endl;
		cout<<"Mergeh <Destination> <source> [Meta Number]"<<endl;
		return 0;
	}else if(argc == 4){
		nMetaSplit = atoi(argv[3]);
	}
	cout<<"Start resaving"<<endl;
   	HANDLE hDestFile = CreateFile(argv[1],GENERIC_READ|GENERIC_WRITE,
	   FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_ALWAYS,FILE_FLAG_SEQUENTIAL_SCAN,NULL);
	SYSTEM_INFO SysInfo;
	GetSystemInfo(&SysInfo);
	DWORD dwGran = SysInfo.dwAllocationGranularity;
	cout<<"Granularity: "<<dwGran<<endl<<"Working..."<<endl;
	DWORD dwFileSizeHigh,dwFileSize;
	__int64 qwFileOffset = 0,qwDestFileOffset = 0,qwDestFileSize = GetAllSize(argv[2],&nSplitNum),qwFileSize;
	cout<<"New File Size: "<<qwDestFileSize<<" bytes"<<endl; 
	HANDLE hDestMapFile = CreateFileMapping(hDestFile,NULL,PAGE_READWRITE,(DWORD)(qwDestFileSize >> 32),(DWORD)(qwDestFileSize & 0xFFFFFFFF),NULL);
	if(!hDestMapFile){
		cout<<"Create Destination Map File failed"<<endl;
		return 0;
	}
	for(int nCount = 0;nCount < nSplitNum;++nCount){
		ostringstream ostr;
		ostr<<argv[2]<<".split.part"<<nCount + 1;
		cout<<endl<<"File: "<<ostr.str()<<" ("<<nCount + 1<<"/"<<nSplitNum<<")"<<endl;
		HANDLE hFile = CreateFile(ostr.str().c_str(),GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,NULL);
		qwFileSize = GetFileSize(hFile,&dwFileSizeHigh);
		qwFileSize |= ((__int64)(dwFileSizeHigh) << 32);
		cout<<"Size: "<<qwFileSize<<" bytes"<<endl;
		HANDLE hMapFile = CreateFileMapping(hFile,NULL,PAGE_READONLY,0,0,NULL);
		if(!hMapFile){
			cout<<"Create Map File failed"<<endl;
			return 0;
		}
		CloseHandle(hFile);
		DWORD dwBlockSize = (qwFileSize/dwGran/nMetaSplit + 1) * dwGran;
		LPBYTE lpbDestAddress,lpbAddress;
		cout<<"Meta starts... [          ](0/"<<nMetaSplit<<")";
		for(int nCount = qwFileOffset = 0;nCount < nMetaSplit;++nCount){
			if(qwFileSize - qwFileOffset < dwBlockSize)
				dwBlockSize = qwFileSize - qwFileOffset;
			lpbDestAddress = (LPBYTE)MapViewOfFile(hDestMapFile,FILE_MAP_ALL_ACCESS,(DWORD)(qwDestFileOffset >> 32),(DWORD)(qwDestFileOffset & 0xFFFFFFFF),dwBlockSize);
			lpbAddress = (LPBYTE)MapViewOfFile(hMapFile,FILE_MAP_READ,(DWORD)(qwFileOffset >> 32),(DWORD)(qwFileOffset & 0xFFFFFFFF),dwBlockSize);
			CopyMemory(lpbDestAddress,lpbAddress,dwBlockSize);
			qwFileOffset += dwBlockSize;
			qwDestFileOffset += dwBlockSize;
			UnmapViewOfFile(lpbAddress);
			UnmapViewOfFile(lpbDestAddress);
			SetConsoleLoading(nCount + 1,nMetaSplit);
		}
		cout<<endl<<"File: "<<ostr.str()<<" end. Working..."<<endl;
		CloseHandle(hMapFile);
		Sleep(300);
		FlushConsole(0,4);
	}
	cout<<endl<<"Closing file..."<<endl;
	UnmapViewOfFile(hDestMapFile);
	CloseHandle(hDestFile);
	cout<<"End of all, files merging is finished. "<<endl; 
	return 0;
}
